#include "InputFile.hpp"

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif

#ifndef STRICT
#define STRICT
#endif

#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN
#endif

#include <windows.h>

namespace Pdf
{
	InputFile::InputFile(char const * path)
		: m_handle(INVALID_HANDLE_VALUE)
		, m_sector(0)
		, m_pointer(NULL)
	{
		CHAR disk[MAX_PATH + 1];
		if (0 == ::GetVolumePathNameA(path, disk, ARRAYSIZE(disk)))
			throw ::GetLastError();
		if (0 == ::GetDiskFreeSpaceA(disk, NULL, &m_sector, NULL, NULL))
			throw ::GetLastError();
		m_handle = ::CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
		if (INVALID_HANDLE_VALUE == m_handle)
			throw ::GetLastError();
		fetch();
	}

	InputFile::InputFile(wchar_t const * path)
		: m_handle(INVALID_HANDLE_VALUE)
		, m_sector(0)
		, m_pointer(NULL)
	{
		WCHAR disk[MAX_PATH + 1];
		if (0 == ::GetVolumePathNameW(path, disk, ARRAYSIZE(disk)))
			throw ::GetLastError();
		if (0 == ::GetDiskFreeSpaceW(disk, NULL, &m_sector, NULL, NULL))
			throw ::GetLastError();
		m_handle = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
		if (INVALID_HANDLE_VALUE == m_handle)
			throw ::GetLastError();
		fetch();
	}

	InputFile::~InputFile()
	{
		if (INVALID_HANDLE_VALUE != m_handle)
			::CloseHandle(m_handle);
	}

	long long InputFile::size() const
	{
		LARGE_INTEGER length = { 0 };
		::GetFileSizeEx(m_handle, &length);
		return length.QuadPart;
	}

	void InputFile::seek(long long offset)
	{
		LARGE_INTEGER distance = { 0 };
		distance.QuadPart = offset / m_sector;
		if (0 == ::SetFilePointerEx(m_handle, distance, NULL, FILE_BEGIN))
			throw ::GetLastError();
		fetch();
		m_pointer = m_buffer.data() + offset % m_sector;
	}

	char InputFile::peek() const
	{
		return *m_pointer;
	}

	char InputFile::next()
	{
		if (m_pointer == m_buffer.data() + m_buflen)
			fetch();
		return *m_pointer++;
	}

	void InputFile::fetch()
	{
		if (0 == ::ReadFile(m_handle, m_buffer.data(), static_cast<DWORD>(m_buffer.size()), &m_buflen, NULL))
			throw ::GetLastError();
		m_pointer = m_buffer.data();
	}
}